package spas;

import imos.IAct;

import javax.media.j3d.Transform3D;
import javax.vecmath.Matrix3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import utils.ErnestUtils;

import ernest.Ernest;

/**
 * A place is a location in the local space that marks something.
 * @author Olivier
 */
public class Place implements IPlace //, Cloneable
{
	/** A place where an interaction was enacted */
	public static int ENACTION_PLACE = 0;
	/** A place generated by the evocation of a compresence */
	public static int EVOKED_PLACE = 1;
	
	/** A place where an interaction was simulated with no information on what was at this place*/
	public static int UNKNOWN = 2;
	/** A place where an interaction was simulated that was afforded by what was known to be at this place */
	public static int AFFORD = 3;
	/** A place where an interaction was simulated that was consistent with a possible movement */
	public static int DISPLACEMENT = 4;
	/** A place where an interaction was simulated that was inconsistent with what was known to be at this place */
	public static int INCONSISTENT = 5;
	
	public static int REACH = 6;

	//public static int FOCUS = 4;

	private int m_type = ENACTION_PLACE;
	private Point3f m_position = new Point3f();
	private Vector3f m_orientation = new Vector3f(1,0,0);
	
	private int m_clock = 0;
	private int m_value;
	private IAct m_act;
	private IBundle m_bundle;	

	//private int m_stick;
	//private Vector3f m_speed = new Vector3f();
	//private Vector3f m_firstPosition = new Vector3f();
	//private Vector3f m_secondPosition = new Vector3f();
	//private float m_span;

	/**
	 * Create a new place 
	 * (The provided position is cloned so the place can be moved without changing the provided position).
	 * @param bundle The bundle at this place.
	 * @param position This place's position.
	 */
	public Place(IBundle bundle, Vector3f position)
	{
		m_bundle = bundle;
		m_position.set(position);
	}
	
	/**
	 * @param position The place's position.
	 * @param type The place's type.
	 */
	public Place(Point3f position, int type)
	{
		m_position.set(position);
		m_type = type;
	}
	
	/**
	 * Clone a place
	 * Warning: the bundle and act that this place contain are not cloned 
	 * @return The cloned place
	 */
	public IPlace clone() 
	{
		Place clonePlace = null;
		try {
			clonePlace = (Place) super.clone();
		} catch(CloneNotSupportedException cnse) {
			cnse.printStackTrace(System.err);
		}

		// We must clone the objects because they are passed by reference by default
		clonePlace.setPosition(m_position);
		//clonePlace.setSpeed(m_speed);
		//clonePlace.setFirstPosition(m_firstPosition);
		//clonePlace.setSecondPosition(m_secondPosition);

		return clonePlace;
	}
	/**
	 * @param bundle This place's bundle.
	 * @param distance This place's distance.
	 * @param direction This place's direction. 
	 * @param span The span of this bundle at this place.
	 */
	public Place(IBundle bundle, float distance, float direction, float span)
	{
		m_bundle = bundle;
		m_position = new Point3f((float)(distance * Math.cos((double)direction)), (float)(distance * Math.sin((double)direction)), 0f);
		//m_span = span;
	}
	
	public void setBundle(IBundle bundle) 
	{
		m_bundle = bundle;
	}

	public IBundle getBundle() 
	{
		return m_bundle;
	}

	public Point3f getPosition() 
	{
		return m_position;
	}
	
	public void transform(Transform3D transform)
	{
		transform.transform(m_position);
		transform.transform(m_orientation);
	}		
	
	public boolean isInCell(Point3f position)
	{
		boolean ret;
		// Is in the same cell.
		ret = (Math.round(m_position.x) == Math.round(position.x)) && (Math.round(m_position.y) == Math.round(position.y)); 
		
		// Is in the same cell in egocentric polar referential.
		
		// Does not work for the cell behind !!
//		if (m_position.length() < .5f && position.length() < .5f)
//			ret = true;
//		else if (Math.round(ErnestUtils.polarAngle(m_position) / (float)Math.PI * 4) ==
// 			     Math.round(ErnestUtils.polarAngle(  position) / (float)Math.PI * 4) &&
// 			     (Math.round(m_position.length()) == Math.round(position.length())))
//			ret = true;
//		else 
//			ret = false;
		
		return ret;		
	}

	/**
	 * Places are equal if they have the same position modulo the LOCATION_RADIUS.
	 * and the same updateCount 
	 */
	public boolean equals(Object o)
	{
		boolean ret = false;
		
		if (o == this)
			ret = true;
		else if (o == null)
			ret = false;
		else if (!o.getClass().equals(this.getClass()))
			ret = false;
		else
		{
			IPlace other = (IPlace)o;
			//ret = (other.getBundle() == m_bundle) && (m_position.epsilonEquals(other.getPosition(), LocalSpace.LOCATION_RADIUS));
			ret = isInCell(other.getPosition()) && other.getClock() == getClock() && other.getType() == getType();
		}		
		return ret;
	}

//	public float getDirection() 
//	{
//		return ErnestUtils.polarAngle(new Vector3f(m_position));
//	}

	public float getDistance() 
	{
		return m_position.distance(new Point3f());
	}
//	public float getSpan() 
//	{
//		return m_span;
//	}
//
//	public void setSpan(float span) 
//	{
//		m_span = span;
//	}
	
	public void setPosition(Point3f position) 
	{
		// Create a new instance of the vector because it is needed by the clone method.
		m_position = new Point3f(position);
		//m_position.set(position);
	}

	public void setType(int type) 
	{
		m_type = type;
	}

	public int getType() 
	{
		return m_type;
	}

//	public void setSpeed(Vector3f speed) 
//	{
//		m_speed = new Vector3f(speed);
//	}
//
//	public Vector3f getSpeed() 
//	{
//		return m_speed;
//	}

//	public void setFirstPosition(Vector3f position) 
//	{
//		m_firstPosition = new Vector3f(position);
//	}
//
//	public void setSecondPosition(Vector3f position) 
//	{
//		m_secondPosition = new Vector3f(position);		
//	}
//
//	public Vector3f getFirstPosition() 
//	{
//		return m_firstPosition;
//	}
//
//	public Vector3f getSecondPosition() 
//	{
//		return m_secondPosition;
//	}

	public void setClock(int clock) 
	{
		m_clock = clock;
	}

	public int getClock() 
	{
		return m_clock;
	}

	public boolean from(Vector3f position) 
	{
		boolean from = false;
		Vector3f compare = new Vector3f(m_position);
		
		// TODO Should take the differential speed of mobile objects into account.
		// (not the speed due to Ernest's movement but the part that is due to the object's movement)
		//if (m_speed != null) compare.sub(m_speed);
		
		compare.sub(position);
		if (compare.length() < .2f) 
			from = true;
		
		return from;
	}

//	public void setStick(int stick) 
//	{
//		m_stick = stick;
//	}
//
//	public int getStick() 
//	{
//		return m_stick;
//	}

	public void setOrientation(float orientation) 
	{
		//m_orientationAngle = orientation;
		m_orientation.set((float) Math.cos(orientation), (float) Math.sin(orientation), 0);
	}
	


	public float getOrientationAngle() 
	{
		//return m_orientationAngle;
		return ErnestUtils.polarAngle(m_orientation);
	}
	
//	public float getFrontDistance()
//	{
//		return m_firstPosition.x - m_firstPosition.y * (m_firstPosition.x - m_secondPosition.x)/(m_firstPosition.y - m_secondPosition.y);
//		
//	}

	public void setValue(int value) 
	{
		m_value = value;
	}

	public int getValue() 
	{
		return m_value;
	}

	public void setAct(IAct act) 
	{
		m_act = act;
	}

	public IAct getAct() 
	{
		return m_act;
	}

	public void setOrientation(Vector3f orientation) 
	{
		m_orientation.set(orientation);
	}

	public Vector3f getOrientation() 
	{
		return m_orientation;
	}

}
